package com.wn.network.flow

import com.blankj.utilcode.util.LogUtils
import com.wn.network.error.ApiException
import com.wn.network.error.ExceptionHandler
import com.wn.network.response.BaseResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withTimeout

/**
 * @Author: wn
 * @Date: 2024/7/25 17:03
 * @Description: flow请求 函数封装
 */
/**
 * 通过 flow 执行请求
 * @param errorCall Function2<Int?, String?, Unit>?       -- 向上传递 code 与 msg
 * @param showLoading Function1<Boolean, Unit>?           -- 向上传递 isLoading
 * @param requestCall SuspendFunction0<BaseResponse<T>?>  -- 向下传递 API-->BaseResponse<T>
 * @return 数据流 -- Flow<T?>
 */
suspend fun <T> requestFlowResponse(
  errorCall: ((Int?, String?) -> Unit)? = null,
  showLoading: ((Boolean) -> Unit)? = null,
  requestCall: suspend () -> BaseResponse<T>?,
): Flow<T?> = flow {
  //  1.执行请求
  // 设置超时时间
  val response = withTimeout(10 * 1000) {
    requestCall()
  }?.apply {
    if (isFailed()) throw ApiException(errorCode, errorMsg)
  }
  // 2.发送网络请求结果回调
  emit(response)
}
  .flowOn(Dispatchers.IO) // 3.指定运行的线程为 IO线程
  .onStart {
    showLoading?.invoke(true) // 4.请求开始，展示加载框
  }
  .map {
    it?.data // 转换 BaseResponse<T>? --> T?
  }
  .catch {
    // 5.捕获异常
    it.printStackTrace()
    ExceptionHandler.handleException(it)
      .apply {
        LogUtils.e(errMsg)
        errorCall?.invoke(errCode, errMsg)
      }
  }
  .onCompletion {
    showLoading?.invoke(false) // 6.请求完成，包括成功和失败 取消加载框
  }

/**
 * 调用 collect获取 emit() 回调的结果，就是请求最后的结果
 * @param errorCall Function2<Int?, String?, Unit>?        -- 向上传递 code 与 msg
 * @param showLoading Function1<Boolean, Unit>?            -- 向上传递 isLoading
 * @param requestCall SuspendFunction0<BaseResponse<T>?>   -- 向下传递 API-->BaseResponse<T>
 * @return 请求最后的结果 -- T?
 */
suspend fun <T> requestFlow(
  errorCall: ((Int?, String?) -> Unit)? = null,
  showLoading: ((Boolean) -> Unit)? = null,
  requestCall: suspend () -> BaseResponse<T>?,
): T? {
  var data: T? = null
  requestFlowResponse(errorCall, showLoading, requestCall)
    .collect {
      data = it
    }
  return data
}

/**
 * 获取 StateFlow<T?>
 * @param scope CoroutineScope                              -- 协程作用域 一般 viewModelScope
 * @param errorCall Function2<Int?, String?, Unit>?         -- 向上传递 code 与 msg
 * @param showLoading Function1<Boolean, Unit>?             -- 向上传递 isLoading
 * @param requestCall SuspendFunction0<BaseResponse<T>?>    -- 向下传递 API --> BaseResponse<T>
 * @return 粘性 热流 -- StateFlow<T?>
 */
fun <T> getStateFlow(
  scope: CoroutineScope,
  errorCall: ((Int?, String?) -> Unit)? = null,
  showLoading: ((Boolean) -> Unit)? = null,
  requestCall: suspend () -> BaseResponse<T>?,
): StateFlow<T?> = flow {
  //  1.执行请求
  runCatching {
    requestCall()
  }
    .onSuccess {
      it?.apply {
        if (isFailed()) throw ApiException(errorCode, errorMsg)
      }
      // 2.发送网络请求结果回调
      emit(it)
    }
    .onFailure {
      throw it
    }
}
  .flowOn(Dispatchers.IO) // 3.指定运行的线程为 IO线程
  .onStart {
    showLoading?.invoke(true) // 4.请求开始，展示加载框
  }
  .map {
    it?.data // 转换 BaseResponse<T>? --> T?
  }
  .catch {
    // 5.捕获异常
    it.printStackTrace()
    ExceptionHandler.handleException(it).apply {
      LogUtils.e(errMsg)
      errorCall?.invoke(errCode, errMsg)
    }
  }
  .onCompletion {
    showLoading?.invoke(false) // 6.请求完成，包括成功和失败 取消加载框
  }
  .stateIn(scope, SharingStarted.WhileSubscribed(5000L), null)

/**
 * 获取 StateFlow<T?>
 * @param scope CoroutineScope                              -- 协程作用域 一般 viewModelScope
 * @param errorCall Function2<Int?, String?, Unit>?         -- 向上传递 code 与 msg
 * @param showLoading Function1<Boolean, Unit>?             -- 向上传递 isLoading
 * @param requestCall SuspendFunction0<BaseResponse<T>?>    -- 向下传递 API --> BaseResponse<T>
 * @return 粘性 热流 -- StateFlow<T?>
 */
fun <T> getStateFlowLocal(
  scope: CoroutineScope,
  errorCall: ((Int?, String?) -> Unit)? = null,
  showLoading: ((Boolean) -> Unit)? = null,
  requestCall: suspend () -> T?,
): StateFlow<T?> = flow {
  //  1.执行请求
  runCatching {
    requestCall()
  }
    .onSuccess {
      // 2.发送网络请求结果回调
      emit(it)
    }
    .onFailure {
      throw it
    }
}
  .flowOn(Dispatchers.IO) // 3.指定运行的线程为 IO线程
  .onStart {
    showLoading?.invoke(true) // 4.请求开始，展示加载框
  }
  .catch {
    // 5.捕获异常
    it.printStackTrace()
    ExceptionHandler.handleException(it).apply {
      LogUtils.e(errMsg)
      errorCall?.invoke(errCode, errMsg)
    }
  }
  .onCompletion {
    showLoading?.invoke(false) // 6.请求完成，包括成功和失败 取消加载框
  }
  .stateIn(scope, SharingStarted.WhileSubscribed(5000L), null)

fun <T> getFlow(
  errorCall: ((Int?, String?) -> Unit)? = null,
  showLoading: ((Boolean) -> Unit)? = null,
  requestCall: suspend () -> BaseResponse<T>?,
): Flow<T?> = flow {
  //  1.执行请求
  runCatching {
    requestCall()
  }
    .onSuccess {
      it?.apply {
        if (isFailed()) throw ApiException(errorCode, errorMsg)
      }
      // 2.发送网络请求结果回调
      emit(it)
    }
    .onFailure {
      throw it
    }
}
  .flowOn(Dispatchers.IO) // 3.指定运行的线程为 IO线程
  .onStart {
    showLoading?.invoke(true) // 4.请求开始，展示加载框
  }
  .map {
    it?.data // 转换 BaseResponse<T>? --> T?
  }
  .catch {
    // 5.捕获异常
    it.printStackTrace()
    ExceptionHandler.handleException(it).apply {
      LogUtils.e(errMsg)
      errorCall?.invoke(errCode, errMsg)
    }
  }
  .onCompletion {
    showLoading?.invoke(false) // 6.请求完成，包括成功和失败 取消加载框
  }
